
// ===============================================================================================================
// -*- C++ -*-
//
// ObjectPool.inl - Template class for a memory pool of objects.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

// If defined to 1, chunks are filled by default with 'chunkFill', else, if defined to 0, no memory fill is performed.
#define POOL_FILL_CHUNKS 0

// Value used to fill the memory chunk bytes if POOL_FILL_CHUNKS is set.
static const int chunkFill = 0;

// =========================================================
// ObjectPool Class Implementation
// =========================================================

template<typename T>
	inline ObjectPool<T>::ObjectPool(void)
{
	list = 0;
	trash = 0;

	chunkSize = ((sizeof(T) + (sizeof(void *)-1)) & (~(sizeof(void *)-1))); // Round the size
	logSize = LOGSIZE_MIN;

	bytesLeft  = 0;
	chunkCount = 0;
}

template<typename T>
	inline ObjectPool<T>::~ObjectPool(void)
{
	Purge(); // Purge all memory.
}

template<typename T>
	inline T * ObjectPool<T>::Allocate(void)
{
	Chunk * tmp;

	if (bytesLeft)
	{
		// We have bytes left from the last allocation, so return it:

		bytesLeft -= chunkSize;
		tmp = reinterpret_cast<Chunk *>(reinterpret_cast<unsigned char *>(list + 1) + bytesLeft);

		#if (POOL_FILL_CHUNKS)
		memset(tmp, chunkFill, chunkSize);
		#endif // POOL_FILL_CHUNKS

		++chunkCount;
	}
	else
	{
		if (trash)
		{
			// Pull a node off the trash heap:

			tmp = trash;
			trash = tmp->next;

			#if (POOL_FILL_CHUNKS)
			memset(tmp, chunkFill, chunkSize);
			#endif // POOL_FILL_CHUNKS

			++chunkCount;
		}
		else
		{
			// Allocate a new block of nodes:

			bytesLeft = (chunkSize * (static_cast<SizeType>(1) << logSize));

			if (bytesLeft < LOGSIZE_MAX)
			{
				++logSize;
			}

			tmp = reinterpret_cast<Chunk *>(MemAlloc(sizeof(Chunk) + bytesLeft));

			if (tmp != 0)
			{
				// Proceed if allocation ok:

				tmp->next = list;
				list = tmp;

				bytesLeft -= chunkSize;
				tmp = reinterpret_cast<Chunk *>(reinterpret_cast<unsigned char *>(list + 1) + bytesLeft);

				#if (POOL_FILL_CHUNKS)
				memset(tmp, chunkFill, chunkSize);
				#endif // POOL_FILL_CHUNKS

				++chunkCount;
			}
		}
	}

	return (reinterpret_cast<T *>(tmp));
}

template<typename T>
	inline void ObjectPool<T>::Free(T * object)
{
	// Move this chunk to the trash heap, but don't delete it.

	if (object != 0)
	{
		(reinterpret_cast<Chunk *>(object))->next = trash;
		trash = reinterpret_cast<Chunk *>(object);
		--chunkCount; // Decrement the number of user chunks.
	}
}

template<typename T>
	inline typename ObjectPool<T>::SizeType ObjectPool<T>::Purge(void)
{
	// Free all memory chunks. Return number of chunks deleted.

	Chunk * tmp = list;

	if (tmp)
	{
		while (list)
		{
			tmp = list->next;
			MemFree(list);
			list = tmp;
		}
	}

	const SizeType oldSize = chunkCount;

	// Reset states
	trash = 0;
	logSize = LOGSIZE_MIN;
	bytesLeft = 0;
	chunkCount = 0;

	return (oldSize); // All chunks destroyed, return the old size.
}

template<typename T>
	inline typename ObjectPool<T>::SizeType ObjectPool<T>::NumberOfObjects(void) const
{
	return (chunkCount);
}